


var EXPORTED_SYMBOLS = ["FIO"];


let FIO = (function() {
    "use strict";

    const Cu = Components.utils;
    const Ci = Components.interfaces;
    const Cc = Components.classes;
    function require(url) {
        let scope = {};
        Cu.import(url, scope);
        return scope;
    }
    let {MiscUtils} = require("chrome://jsm/content/utils.js");
    let {Prefs} = require("chrome://jsm/content/Preferences.js");
    let {Services} = require("resource://gre/modules/Services.jsm");
    let {Task} = require("resource://gre/modules/Task.jsm");
    let {OS} = require("resource://gre/modules/osfile.jsm");

    return {
        get pathSeparator() {
            return MiscUtils.isWindows() ? "\\" : "/";
        },

        isFullPath(path) {
            return OS.Path.split(path).absolute;
        },

        
        
        getMacroPath(macro_path) {
            return OS.Path.join(Prefs.defaultMacroPath, 
                                ...OS.Path.split(macro_path).components);
        },
        

        
        moveDirectory(src, dst) {
            let self = this;
            let it = new OS.File.DirectoryIterator(src);
            return Task.spawn(function* () {
                yield OS.File.makeDir(dst, {ignoreExisting: false});
                while(true) {
                    let entry = yield it.next();
                    if (entry.isDir) {
                        yield self.moveDirectory(
                            entry.path, OS.Path.join(dst, entry.name)
                        );
                    } else {
                        yield OS.File.move(
                            entry.path, 
                            OS.Path.join(dst, entry.name)
                        );
                    }
                }
            }).catch(e => {
                it.close();
                if (e != StopIteration)
                    throw e;
                OS.File.removeDir(src);
            });
        },
        
        
        copyDirectory(src, dst) {
            let self = this;
            let it = new OS.File.DirectoryIterator(src);
            return Task.spawn(function* () {
                yield OS.File.makeDir(dst);
                while(true) {
                    let entry = yield it.next();
                    if (entry.isDir) {
                        yield self.copyDirectory(
                            entry.path, OS.Path.join(dst, entry.name)
                        );
                    } else {
                        yield OS.File.copy(
                            entry.path, 
                            OS.Path.join(dst, entry.name)
                        );
                    }
                }
            }).catch(e => {
                it.close();
                if (e != StopIteration)
                    throw e;
            });
        },

        get encoder() {
            if (!this.utf8_encoder)
                this.utf8_encoder = new TextEncoder("utf-8");
            return this.utf8_encoder;
        },
        
        utf8_mask: Uint8Array.from([239, 187, 191]),
        utf32be_mask: Uint8Array.from([0, 0, 254, 255]),
        utf32le_mask: Uint8Array.from([255, 254, 0, 0]),
        utf16le_mask: Uint8Array.from([255, 254]),
        utf16be_mask: Uint8Array.from([254, 255]),
        

        
        
        detectBOM(uint8_array) {
            function equals(a, b) {
                if (a.length > b.length)
                    return false;
                return !a.some((x, i) => x !== b[i]);
            };
            
            if (uint8_array.length == 2) {
                let sub = uint8_array.subarray(0, 2);
                if (equals(this.utf16be_mask, sub))
                    return "utf16-be";
                if (equals(this.utf16le_mask, sub))
                    return "utf16-le";
            } else if (uint8_array.length == 3) {
                let sub = uint8_array.subarray(0, 3);
                if (equals(this.utf8_mask, sub))
                    return "utf-8";
            } else if (uint8_array.length > 3) {
                let sub = uint8_array.subarray(0, 4);
                if (equals(this.utf8_mask, sub)) {
                    return "utf-8";
                } else if (equals(this.utf32be_mask, sub)) {
                    return "utf-32be";
                } else if (equals(this.utf32le_mask, sub)) {
                    return "utf-32le";
                } else if (equals(this.utf16le_mask, sub)) {
                    return "utf-16le";
                } else if (equals(this.utf16be_mask, sub)) {
                    return "utf-16be";
                } 
            }
            
            return "unknown";
        },

        
        
        
        
        _win_CP_to_charset_name(cp) {
            switch(cp) {
            case 37: return "ibm037";
            case 437: return "ibm437";
            case 500: return "ibm500";
            case 708: return "asmo-708";
            case 709: return "asmo";
            case 710: return "asmo";
            case 720: return "dos-720";
            case 737: return "ibm737";
            case 775: return "ibm775";
            case 850: return "ibm850";
            case 852: return "ibm852";
            case 855: return "ibm855";
            case 857: return "ibm857";
            case 858: return "ibm00858";
            case 860: return "ibm860";
            case 861: return "ibm861";
            case 862: return "dos-862";
            case 863: return "ibm863";
            case 864: return "ibm864";
            case 865: return "ibm865";
            case 866: return "cp866";
            case 869: return "ibm869";
            case 870: return "ibm870";
            case 874: return "windows-874";
            case 875: return "cp875";
            case 932: return "shift_jis";
            case 936: return "gb2312";
            case 949: return "ks_c_5601-1987";
            case 950: return "big5";
            case 1026: return "ibm1026";
            case 1047: return "ibm01047";
            case 1140: return "ibm01140";
            case 1141: return "ibm01141";
            case 1142: return "ibm01142";
            case 1143: return "ibm01143";
            case 1144: return "ibm01144";
            case 1145: return "ibm01145";
            case 1146: return "ibm01146";
            case 1147: return "ibm01147";
            case 1148: return "ibm01148";
            case 1149: return "ibm01149";
            case 1200: return "utf-16";
            case 1201: return "unicodefffe";
            case 1250: return "windows-1250";
            case 1251: return "windows-1251";
            case 1252: return "windows-1252";
            case 1253: return "windows-1253";
            case 1254: return "windows-1254";
            case 1255: return "windows-1255";
            case 1256: return "windows-1256";
            case 1257: return "windows-1257";
            case 1258: return "windows-1258";
            case 1361: return "johab";
            case 10000: return "macintosh";
            case 10001: return "x-mac-japanese";
            case 10002: return "x-mac-chinesetrad";
            case 10003: return "x-mac-korean";
            case 10004: return "x-mac-arabic";
            case 10005: return "x-mac-hebrew";
            case 10006: return "x-mac-greek";
            case 10007: return "x-mac-cyrillic";
            case 10008: return "x-mac-chinesesimp";
            case 10010: return "x-mac-romanian";
            case 10017: return "x-mac-ukrainian";
            case 10021: return "x-mac-thai";
            case 10029: return "x-mac-ce";
            case 10079: return "x-mac-icelandic";
            case 10081: return "x-mac-turkish";
            case 10082: return "x-mac-croatian";
            case 12000: return "utf-32";
            case 12001: return "utf-32be";
            case 20000: return "x-chinese_cns";
            case 20001: return "x-cp20001";
            case 20002: return "x_chinese-eten";
            case 20003: return "x-cp20003";
            case 20004: return "x-cp20004";
            case 20005: return "x-cp20005";
            case 20105: return "x-ia5";
            case 20106: return "x-ia5-german";
            case 20107: return "x-ia5-swedish";
            case 20108: return "x-ia5-norwegian";
            case 20127: return "us-ascii";
            case 20261: return "x-cp20261";
            case 20269: return "x-cp20269";
            case 20273: return "ibm273";
            case 20277: return "ibm277";
            case 20278: return "ibm278";
            case 20280: return "ibm280";
            case 20284: return "ibm284";
            case 20285: return "ibm285";
            case 20290: return "ibm290";
            case 20297: return "ibm297";
            case 20420: return "ibm420";
            case 20423: return "ibm423";
            case 20424: return "ibm424";
            case 20833: return "x-ebcdic-koreanextended";
            case 20838: return "ibm-thai";
            case 20866: return "koi8-r";
            case 20871: return "ibm871";
            case 20880: return "ibm880";
            case 20905: return "ibm905";
            case 20924: return "ibm00924";
            case 20932: return "euc-jp";
            case 20936: return "x-cp20936";
            case 20949: return "x-cp20949";
            case 21025: return "cp1025";
            case 21866: return "koi8-u";
            case 28591: return "iso-8859-1";
            case 28592: return "iso-8859-2";
            case 28593: return "iso-8859-3";
            case 28594: return "iso-8859-4";
            case 28595: return "iso-8859-5";
            case 28596: return "iso-8859-6";
            case 28597: return "iso-8859-7";
            case 28598: return "iso-8859-8";
            case 28599: return "iso-8859-9";
            case 28603: return "iso-8859-13";
            case 28605: return "iso-8859-15";
            case 29001: return "x-europa";
            case 38598: return "iso-8859-8-i";
            case 50220: return "iso-2022-jp";
            case 50221: return "csiso2022jp";
            case 50222: return "iso-2022-jp";
            case 50225: return "iso-2022-kr";
            case 50227: return "x-cp50227";
            case 50229: return "iso";
            case 50930: return "ebcdic";
            case 50931: return "ebcdic";
            case 50933: return "ebcdic";
            case 50935: return "ebcdic";
            case 50936: return "ebcdic";
            case 50937: return "ebcdic";
            case 50939: return "ebcdic";
            case 51932: return "euc-jp";
            case 51936: return "euc-cn";
            case 51949: return "euc-kr";
            case 51950: return "euc";
            case 52936: return "hz-gb-2312";
            case 54936: return "gb18030";
            case 57002: return "x-iscii-de";
            case 57003: return "x-iscii-be";
            case 57004: return "x-iscii-ta";
            case 57005: return "x-iscii-te";
            case 57006: return "x-iscii-as";
            case 57007: return "x-iscii-or";
            case 57008: return "x-iscii-ka";
            case 57009: return "x-iscii-ma";
            case 57010: return "x-iscii-gu";
            case 57011: return "x-iscii-pa";
            case 65000: return "utf-7";
            case 65001: return "utf-8";
            default:
                return "unknown";
            };
        },

        _ansi_cp: null,

        get ANSI_CP() {
            if (!this._ansi_cp) {
                
                
                let {ctypes} = require("resource://gre/modules/ctypes.jsm");
                let kernel32_dll = ctypes.open("kernel32.dll");
                let GetACP = kernel32_dll.declare(
                    "GetACP", ctypes.default_abi, ctypes.uint32_t
                );
                let codepage = GetACP();
                kernel32_dll.close();
                this._ansi_cp = this._win_CP_to_charset_name(codepage);
            }

            return this._ansi_cp;
        },

        
        readTextFile(file_path) {
            return OS.File.read(file_path).then(data => {
                let utf_encoding = this.detectBOM(data);
                if (utf_encoding != "unknown")
                    return (
                        new TextDecoder(utf_encoding, {fatal: true})
                    ).decode(data);

                let encoding_list = [];
                if (MiscUtils.isWindows()) {
                    encoding_list = [this.ANSI_CP];
                }

                encoding_list = encoding_list.concat(
                    ["utf-8", "utf-16", "utf-32"]
                );
                for (let encoding of encoding_list) {
                    let decoder = new TextDecoder(encoding, {fatal: true});
                    try {
                        return decoder.decode(data);
                    } catch(e) {
                        continue; 
                    }
                }
                
                throw new Error("Unable to detect the file ("+file_path+
                                ") encoding");
            });
        },


        
        writeTextFile(file_path,  text) {
            
            
            
            let encoder = this.encoder;
            return Task.spawn(function *() {
                let file = yield OS.File.open(
                    file_path, {write: true, append: false, truncate: true}
                );
                try {
                    
                    yield file.write(Uint8Array.from([239, 187, 191]));
                    yield file.write(encoder.encode(text));
                } catch(e) {
                    throw e;    
                } finally {
                    file.close();
                }
            });
        },

        
        appendTextFile(file_path,  text) {
            let encoder = this.encoder;
            return Task.spawn(function *() {
                let exists = yield OS.File.exists(file_path);
                let file = yield OS.File.open(
                    file_path, {write: true, append: true}
                );
                try {
                    if (!exists) {
                        
                        yield file.write(Uint8Array.from([239, 187, 191]));
                    }
                    yield file.write(encoder.encode(text));
                } catch(e) {
                    throw e;    
                } finally {
                    file.close()
                }
            });
        }

    };
}) ();
